/*
 * posix_sem_3.c
 *
 *  Created on: 24/11/2014
 *      Author: dario
 */
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h> /* fork function */
#include <stdlib.h> /* exit function and others */
#include <sys/ipc.h> /* for communication */
#include <sys/shm.h> /* Shared memory functions */
#include <sys/stat.h>
#include <sys/wait.h>
#include <semaphore.h> /* POSIX Semaphores */
#include <sys/types.h>
#include "control.h"

//#define USE_SIMULA_MUERTE_SERVIDOR
#define USE_SIMULA_MUERTE_MONITOR

typedef struct {
	pid_t pid;
	char respuesta;
} MonitorStruct;

MonitorStruct *datoMonitorEnMemoria;
int memoria_id; /* identificador generado por la petición de memoria */

char *sem_name_A;
sem_t *SEM_A;

int constantTest;
int contadorExistencia;

pid_t child_process;

void *iniciarControl(void *p_arg) {

	sem_name_A = (char*) malloc(sizeof(char) * 12);
	strcpy(sem_name_A, "/semMonitor");
	/* Simulación lo que sería codigo del Servidor de Torneo */
	constantTest = 0;
	contadorExistencia = 0;

	freeSemaphores(sem_name_A); /* Destruyo el semaforo si ya existia. */

	/* Create and initialize semaphore,
	 * SEM_A_INI: if it is 0 is block. if it is 1, can be take control of him. */
	int SEM_A_INI = 1;
	SEM_A = sem_open(sem_name_A, O_CREAT, 0644, SEM_A_INI);
	if(SEM_A == SEM_FAILED) {
		perror("[ServidorTorneo] Error: unable to create semaphore");
		exit(EXIT_FAILURE);
	}

	/* Conseguimos una clave para la memoria compartida. Todos los procesos que
	 * quieran comparatir memoria, deben obtener la misma clave. Esta se puede
	 * conseguir por medio de la funcion ftok.
	 * A esta función se le pasa un fichero cuaquiera que exista y esté accesible
	 * (todos los procesos deben pasar el mismo fichero) y un entero cualquiera
	 * (todos los procesos el mismo entero) */

	key_t memory_key; /* Clave para memoria compartida */
	char nombre_key[] = "/bin/ls"; /* Para obtener clave de memoria compartida */
	int number_key_mem = 'M';
	memory_key = ftok(nombre_key, number_key_mem); /* obtengo la clave. */
	if(memory_key == -1) {
		printf("[ServidorTorneo] No consigo clave para memoria compartida\n");
		exit(EXIT_FAILURE);
	}

	/* Creamos la memoria con la clave recién conseguida. Para ello llamamos
	 * a la función shmget pasándole
	 * la #clave#.
	 * el #tamaño de memoria#
	 * unos #flags#:
	 * Los flags son los permisos de lect/escr/ejec para el propietario, grupo y otros
	 * (es el 777 en octal) y el flag IPC_CREAT Para indicar que cree la memoria.
	 * La función nos devuelve un identificador para la memoria recién creada. */
	memoria_id = shmget(memory_key, sizeof(MonitorStruct), 0777 | IPC_CREAT);
	if(memoria_id == -1) {
		printf("[Cliente] No consigo Id para memoria compartida\n");
		exit(EXIT_FAILURE);
	}
	printf("[ServidorTorneo] memory _id: %d\n", memoria_id);
	printf("[ServidorTorneo] semaforo: semMonitor\n");

	/* Una vez creada la memoria, hacemos que uno de nuestros punteros apunte a la zona
	 * de memoria recién creada. Para ello llamamos a shmat, pasándole el identificador
	 * obtenido anteriormente y un par de parámetros extraños, que con ceros vale. */
	datoMonitorEnMemoria = (MonitorStruct *) shmat(memoria_id, (char*)NULL, 0777);
	if(datoMonitorEnMemoria == NULL) {
		printf("[ServidorTorneo] No consigo memoria compartida\n");
		exit(EXIT_FAILURE);
	}
	printf(" [ServidorTorneo] Direccion de memoria: %p\n", datoMonitorEnMemoria);
	child_process = fork();
	if(child_process < -1) {
		printf("[ServidorTorneo] Error al crear procesos\n");
	} else if(child_process == 0) {
		/* leo la memoria compartida en el otro proceso. */
		char programName[] = "Monitor";
		char *program[] = { programName, (char*) NULL };
		execvp(MONITOR_PATH, program);
		fprintf(stderr, "[ServidorTorneo] Error al ejecutar Monitor\n");
		exit(EXIT_FAILURE);
	}
	sleep(2); /* la primera vez espero a que inicie el monitor */
	while(1) {
		sleep(2); /* Esto prodría equivaler a hacer otra cosa mientras hasta que llege la señal del monitor */
		monitorConsultHandler(2);
	}
	printf("[ServidorTorneo] FIN PROCESO PADRE\n");

	return (void*)NULL;
}


void freeSemaphores(char * semaphoreName) {
	/* Create and initialize semaphore,
	 * val_ini_sem: if it is 0 is block. if it is 1, can be take control of him. */
	sem_t * semaphore = sem_open(semaphoreName, 0);
	if(semaphore == SEM_FAILED) {
		return;
	}

	/* desasocio el semaforo. */
	sem_close(semaphore);

	/* destruyo el semaforo */
	sem_unlink(semaphoreName);
}

void monitorConsultHandler(int sign) {
	constantTest++;
	/* Ya podemos utilizar la memoria.
	 * Escribimos cosas en la memoria. Los números de 1 a 10 esperando un segundo entre ellos.
	 * Estos datos serán los que lea el otro proceso. */
	int pidPadre = getpid();
	printf("\n[ServidorTorneo] Pid: %d, CHILD_PROCESS: %d\n", pidPadre, child_process);
	printf(" [ServidorTorneo] Direccion de memoria: %p\n", datoMonitorEnMemoria);
	/* ------------------- Start of Critical Region -------------------- */
	printf("[ServidorTorneo] Linea 147. Consulta %d: Respondo al Monitor que estoy vivo y verifico si existe también.\n", constantTest);
	int resp = waitpid(child_process, NULL, WNOHANG | WUNTRACED);
	if (resp != 0) {
		fprintf(stderr, "\n[ServidorTorneo] Monitor muerto, por lo tanto, lo reinicio. PID->MONITOR: %d\n", resp);
		reiniciarMonitor(0);
		return;
	} else {
		printf("[ServidorTorneo] Monitor está vivo -> %d", resp);
	}
	if (sem_wait(SEM_A) == -1) { /* Semaphore is up */
		fprintf(stderr, "[ServidorTorneo] Process faild to unlock semaphore\n");
		exit(EXIT_FAILURE);
	}
#ifdef USE_SIMULA_MUERTE_SERVIDOR
	if(constantTest < 8) {
		printf("Consulta %d: Respondo al Monitor si estoy vivo \n", constantTest);
		datoMonitorEnMemoria->pid = getpid();
		datoMonitorEnMemoria->respuesta = 'S';
	} else {
		printf("\nFinjo estar muerto\n");
	}

#endif

#ifdef USE_SIMULA_MUERTE_MONITOR
	datoMonitorEnMemoria->pid = pidPadre;
	datoMonitorEnMemoria->respuesta = 'S';
#endif
	/* -------------------- End of Critical Region --------------------- */

	/*------------- Free a semaphore to use Shared Memory -------------*/
	if (sem_post(SEM_A) == -1) { /* Semaphore is up */
		fprintf(stderr, "[ServidorTorneo] Process failed to unlock semaphore\n");
		exit(EXIT_FAILURE);
	}
#ifdef USE_SIMULA_MUERTE_SERVIDOR
	if(constantTest== 8) {
		sleep(4);
		reiniciarMonitor(0);
	}
#endif

}

void closeMonitorResources() {
	wait(NULL); /* wait for dead child */
	/* Terminada de usar la memoria compartida, la liberamos. */
	shmdt((void*)datoMonitorEnMemoria); /* Desasocia la memoria compartida de la zona de nuestro programa. Solo un proceso. */
	shmctl(memoria_id, IPC_RMID, (struct shmid_ds *)NULL); /* Destruye la zona de MComp. Solo un proceso. */

	sem_close(SEM_A);/* desasocio el semaforo. */
	sem_unlink(sem_name_A);/* destruyo el semaforo */
}

void reiniciarMonitor(int sign) {
#ifdef USE_SIMULA_MUERTE_SERVIDOR
	printf("Terminando proceso, para simulación de procesos muerto.\n");
	exit(EXIT_FAILURE);
#endif
#ifdef USE_SIMULA_MUERTE_MONITOR
	fprintf(stderr, "[Cliente] Linea 215. Reinicio del monitor\n");
	child_process = fork();
	if (child_process < -1) {
		printf("[ServidorTorneo] Linea 219. Error al crear procesos\n");
	} else if (child_process == 0) {
		/* leo la memoria compartida en el otro proceso. */
		char programName[] = "[Cliente] Monitor";
		char *program[] = { programName, (char*) NULL };
		execvp(MONITOR_PATH, program);
		fprintf(stderr, "[ServidorTorneo] Error al ejecutar Monitor\n");
		exit(EXIT_FAILURE);
	}
	printf("[ServidorTorneo] Reiniciado el monitor\n");
#endif
}
